﻿#include "smallc.h"

vector<string> code;                            // 代码

int linenum;                                    // 当前处理到的行号
int charnum;                                    // 当前处理到的字符下标
int number;                                     // 当前数字
int txind;                                      // 当前符号表大小
int parnum;                                     // 当前函数参数个数
int lvarnum;                                    // 当前函数局部变量个数
int pcnum;                                      // 当前pcode个数
int issuccess;                                  // 是否编译成功


string proname;                                 // 当前标识符所属函数
string linecode;                                // 当前代码行
string id;                                      // 当前ident
string em[errormax];                            // 错误信息


map<string, symbol> word;                        // 保留字

vector<instruction> Pcode;                      // pcode
vector<string> ems;                             // 传给前端的报错信息


char ch;                                        // 最近一次从文件中读出的字符

symbol sym;                                     // 当前符号类型
symbol rwordsym[rwordnum];                      // 保留字对应的符号类型

tablestruct table[txmax];                       // 符号表

/*
* 编译器初始化
*/
void init() {
    linenum = 0;
    charnum = 0;
    ch = ' ';
    txind = 0;
    pcnum = 0;
    sym = nulsym;
    id = "";
    linecode = "";
    number = 0;                                    
    parnum = 0;                                   
    lvarnum = 0;      
    code.clear();
    Pcode.clear();
    ems.clear();
    issuccess = 1;

    word["if"] = ifsym;
    word["else"] = elsesym;
    word["int"] = intsym;
    word["read"] = readsym;
    word["write"] = writesym;
    word["const"] = constsym;
    word["return"] = returnsym;
    word["while"] = whilesym;
    word["case"] = casesym;
    word["switch"] = switchsym;

    em[0] = "缺少分号";
    em[1] = "常数超过指定位数";
    em[2] = "缺少变量类型";
    em[3] = "保留字不能作为标识符";
    em[4] = "无效的标识符";
    em[5] = "缺少赋值符号";
    em[6] = "缺少常量";
    em[7] = "缺少分号";
    em[8] = "缺少左括号";
    em[9] = "缺少右括号";
    em[10] = "缺少左花括号";
    em[11] = "缺少标识符";
    em[12] = "未定义的标识符";
    em[13] = "不能对const变量赋值";
    em[14] = "不能对函数赋值";
    em[15] = "缺少右花括号";
    em[16] = "必须写入int";
    em[17] = "必须输出有效变量";
    em[18] = "switch缺少参数";
    em[19] = "codefile is empty";
    em[20] = "参数数量不匹配";
    em[21] = "无法匹配的花括号";


    em[30] = "无法识别";
}

/*
* 从文件中读取一个字符
*/
void getch() {
    if (charnum == linecode.size() && linenum < code.size() - 1) {
        linecode = code[++linenum];
        charnum = 0;
    }
    if (charnum == linecode.size() && linenum == code.size() - 1) ch = 0;
    else ch = linecode[charnum++];
}

/*
* 从文件中读取一个单词
*/
void getsym() {
    while (ch == ' ' || ch == '\n' || ch == '\t') getch();
    if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
        id = "";
        do {
            id += ch;
            getch();
        } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'));
        if (word.count(id) == 0) {                  // 当前的单词是标识符
            sym = identsym;
        }
        else {
            sym = word[id];
        }
    }
    else if (ch >= '0' && ch <= '9') {
        int len = 0;
        number = 0;
        do {
            len++;
            number = number * 10 + ch - '0';
            getch();
        } while (ch >= '0' && ch <= '9');
        sym = numbersym;
        if (len > nummaxlen) error(1);
    }
    else if (ch == '+') {
        sym = plussym;
        getch();
    }
    else if (ch == '-') {
        sym = minussym;
        getch();
    }
    else if (ch == '*') {
        sym = mulsym;
        getch();
    }
    else if (ch == '/') {
        getch();
        if (ch != '*') sym = divsym;
        else {
            while (true) {
                do {
                    getch();
                } while (ch != '*');
                getch();
                if (ch == '/') {
                    getch();
                    getsym();
                    break;
                }
                if (ch == 0) {
                    sym == nulsym;
                    break;
                }
            }
        }
    }
    else if (ch == '(') {
        sym = lparensym;
        getch();
    }
    else if (ch == ')') {
        sym = rparensym;
        getch();
    }
    else if (ch == ';') {
        sym = semicolonsym;
        getch();
    }
    else if (ch == '=') {
        getch();
        if (ch == '=') {
            sym = eqlsym;
            getch();
        }
        else sym = becomesym;
    }
    else if (ch == '<') {
        getch();
        if (ch == '=') {
            sym = leqsym;
            getch();
        }
        else sym = ltsym;
    }
    else if (ch == '>') {
        getch();
        if (ch == '=') {
            sym = geqsym;
            getch();
        }
        else sym = gtsym;
    }
    else if (ch == '{') {
        sym = beginsym;
        getch();
    }
    else if (ch == '}') {
        sym = endsym;
        getch();
    }
    else if (ch == ',') {
        sym = commasym;
        getch();
    }
    else if (ch == '%') {
        sym = modsym;
        getch();
    }
    else if (ch == '!') {
        getch();
        if (ch == '=') {
            sym = neqsym;
            getch();
        }
        else sym = nulsym;
    }
    else {
        sym = nulsym;
        getch();
    }
}

/*
* 错误处理
*
* errorid 错误码
*/
void error(int errorid) {
    ems.push_back("error " + to_string(linenum + 1) + "行: " + em[errorid]);
    issuccess = 0;
}

/*
* 检查标识符的有效性
*
* kind 标识符类型
*/
bool isvaliddeclaration(object kind) {
    if (word.count(id)) return false;
    for (int i = 0; i < txind; i++) {
        if (kind == constant && table[i].kind == constant && id == table[i].name) return false;
        if (kind == procedure) {
            if (table[i].kind == procedure && id == table[i].name) return false;
            if (table[i].kind == constant && id == table[i].name) return false;
        }
        if (kind == variable) {
            if (table[i].kind == procedure && id == table[i].name) return false;
            if (table[i].kind == variable && id == table[i].name && table[i].proname == proname) return false;
        }
    }
    return true;
}

/*
* 填充符号表
*
* name      名字
* kind      类型 const var procedure
* adr       地址 仅const不使用
* par       参数数量 仅procedure使用
* lvar      局部变量 仅procedure使用
* proname   所属函数 仅var使用
*/
void enter(object kind) {
    if (kind == constant) {
        table[txind].name = id;
        table[txind].kind = kind;
        table[txind].val = number;
    }
    else if (kind == procedure) {
        table[txind].adr = pcnum;
        table[txind].name = id;
        table[txind].kind = kind;
    }
    else {
        table[txind].name = id;
        table[txind].kind = kind;
        table[txind].proname = proname;
        table[txind].adr = parnum + lvarnum;
    }
    txind++;
}

/*
* 回填符号表
*/
void renter() {
    for (int i = txind - 1; i >= 0; i--) {
        if (table[i].kind == procedure) {
            table[i].par = parnum;
            table[i].lvar = lvarnum;
            break;
        }
    }

}

/*
* 处理const声明
*/
void constdeclaration() {
    getsym();
    if (isvaliddeclaration(constant)) {
        getsym();
        if (sym == becomesym) {
            getsym();
            if (sym == numbersym) {
                enter(constant);
                getsym();
                if (sym == commasym) constdeclaration();
                else if (sym == semicolonsym) getsym();
                else error(7);
            }
            else error(6);
        }
        else error(5);
    }
    else {
        error(4);
        while (sym != commasym && sym != semicolonsym) getsym();
        if (sym == commasym) {
            getsym();
            constdeclaration();
        }
        else getsym();
    }
}

/*
* 处理参数声明
*/
void parameterdeclaration() {
    if (sym == intsym) {
        getsym();
        if (sym == identsym) {
            if (word.count(id) == 0) {
                parnum++;
                enter(variable);
                getsym();
                while (sym == commasym) {
                    getsym();
                    parameterdeclaration();
                }
            }
            else error(3);
        }
        else error(11);
    }
    else error(2);
}

/*
* 处理int声明
*/
void intdeclaration() {
    if (sym == identsym) {
        if (word.count(id) == 0) {
            if (isvaliddeclaration(variable)) {
                lvarnum++;
                enter(variable);
                getsym();
                while (sym == commasym) {
                    getsym();
                    intdeclaration();
                }
            }
            else error(4);
        }
        else error(3);
    }
    else error(11);
}

/*
* 查找当前标识符在符号表中的位置
*/
int findintable(string name) {
    for (int i = txind - 1; i >= 0; i--) {
        if (table[i].name == name) {
            if (table[i].kind == constant) return i;
            else if (table[i].kind == procedure) return i;
            else {
                if (table[i].proname == proname) return i;
            }
        }
    }
    return -1;
}

/*
* 处理因子
*/
void factor() {
    if (sym == identsym) {
        int t = findintable(id);
        if (t != -1) {
            if (table[t].kind == constant) {
                gen(LIT, table[t].val);
                getsym();
            }
            else if (table[t].kind == variable) {
                gen(LOD, table[t].adr);
                getsym();
            }
            else {
                getsym();
                if (sym == lparensym) {
                    getsym();
                    int tmp = 0;
                    if (sym != rparensym) {
                        tmp++;
                        expression();
                        while (sym == commasym) {
                            getsym();
                            expression();
                            tmp++;
                        }
                        if (sym == rparensym) getsym();
                        else error(9);
                    }
                    if (tmp == table[t].par) {
                        gen(STP, table[t].par);
                        gen(CAL, table[t].adr);
                    }
                    else error(20);
                }
                else error(8);
            }
        }
        else {
            error(12);
            getsym();
            if (sym == lparensym) {
                int t = 1;
                do {
                    getsym();
                    if (sym == lparensym) t++;
                    if (sym == rparensym) t--;
                } while (t!=0);
                getsym();
            }
        }
    }
    else if (sym == numbersym) {
        gen(LIT, number);
        getsym();
    }
    else if (sym == lparensym) {
        getsym();
        expression();
        if (sym == rparensym) getsym();
        else error(9);
    }

}

/*
* 处理项
*/
void term() {
    factor();
    while (sym == mulsym || sym == divsym || sym == modsym) {
        if (sym == mulsym) {
            getsym();
            factor();
            gen(OPR, 4);
        }
        else if (sym == divsym) {
            getsym();
            factor();
            gen(OPR, 5);
        }
        else if (sym == modsym) {
            getsym();
            factor();
            gen(OPR, 1);
        }
    }
}

/*
* 处理表达式
*/
void expression() {
    if (sym == minussym) {
        gen(LIT, 0);
        getsym();
        term();
        gen(OPR, 3);
        while (sym == plussym || sym == minussym) {
            if (sym == plussym) {
                getsym();
                term();
                gen(OPR, 2);
            }
            else {
                getsym();
                term();
                gen(OPR, 3);
            }
        }
    }
    else {
        term();
        while (sym == plussym || sym == minussym) {
            if (sym == plussym) {
                getsym();
                term();
                gen(OPR, 2);
            }
            else {
                getsym();
                term();
                gen(OPR, 3);
            }
        }
    }

}

/*
* 处理条件句
*/
void condition() {
    expression();
    if (sym == eqlsym) {
        getsym();
        expression();
        gen(OPR, 6);
    }
    else if (sym == neqsym) {
        getsym();
        expression();
        gen(OPR, 7);
    }
    else if (sym == ltsym) {
        getsym();
        expression();
        gen(OPR, 8);
    }
    else if (sym == leqsym) {
        getsym();
        expression();
        gen(OPR, 9);
    }
    else if (sym == gtsym) {
        getsym();
        expression();
        gen(OPR, 10);
    }
    else if (sym == geqsym) {
        getsym();
        expression();
        gen(OPR, 11);
    }
}

/*
* 处理语句
*/
void statement() {
    while (sym != endsym && sym != nulsym) {
        if (sym == identsym) {
            int t = findintable(id);
            if (t != -1) {
                if (table[t].kind == constant) error(13);
                else if (table[t].kind == procedure) error(14);
                else {
                    getsym();
                    if (sym == becomesym) {
                        getsym();
                        expression();
                        if (sym == semicolonsym) {
                            gen(STO, table[t].adr);
                            getsym();
                        }
                        else error(0);
                    }
                    else error(5);
                }
            }
            else error(12);
        }
        else if (sym == whilesym) {
            getsym();
            int t1 = pcnum;
            if (sym == lparensym) {
                getsym();
                condition();
                gen(JPC, 0);
                int t2 = pcnum - 1;
                if (sym == rparensym) {
                    getsym();
                    if (sym == beginsym) {
                        getsym();
                        statement();
                        if (sym == endsym) {
                            gen(JMP, t1);
                            Pcode[t2].a = pcnum;
                            getsym();
                        }
                        else error(15);
                    }
                    else error(10);
                }
                else error(9);
            }
            else error(8);
        }
        else if (sym == switchsym) {
            getsym();
            if (sym == lparensym) {
                getsym();
                if (sym != rparensym) {
                    expression();
                    if (sym == rparensym) {
                        getsym();
                        if (sym == beginsym) {
                            getsym();
                            int t = -1;
                            vector<int>Re;
                            while (sym == casesym) {
                                getsym();
                                if (sym == numbersym) {
                                    if (t != -1) Pcode[t].a = pcnum;
                                    gen(OPR, 15);
                                    gen(LIT, number);
                                    gen(OPR, 6);
                                    gen(JPC, 0);
                                    t = pcnum - 1;
                                    getsym();
                                    if (sym == beginsym) {
                                        getsym();
                                        statement();
                                        gen(JMP, 0);
                                        Re.push_back(pcnum - 1);
                                        if (sym == endsym) getsym();
                                        else error(15);
                                    }
                                    else error(10);
                                }
                                else if (sym == identsym) {
                                    int ind = findintable(id);
                                    if (ind != -1) {
                                        if (table[ind].kind == constant) {
                                            if (t != -1) Pcode[t].a = pcnum;
                                            gen(OPR, 15);
                                            gen(LIT, table[ind].val);
                                            gen(OPR, 6);
                                            gen(JPC, 0);
                                            t = pcnum - 1;
                                            getsym();
                                            if (sym == beginsym) {
                                                getsym();
                                                statement();
                                                gen(JMP, 0);
                                                Re.push_back(pcnum - 1);
                                                if (sym == endsym) getsym();
                                                else error(15);
                                            }
                                            else error(10);
                                        }
                                        else error(6);
                                    }
                                    else error(6);
                                }
                                else error(6);
                            }
                            if (sym == endsym) {
                                Pcode[t].a = pcnum;
                                for (int i = 0; i < Re.size(); i++) Pcode[Re[i]].a = pcnum;
                                gen(OPR, 14);
                                getsym();
                            }
                            else error(15);
                        }
                        else error(10);
                    }
                    else error(9);
                }
                else error(18);
            }
            else error(8);
        }
        else if (sym == readsym) {
            getsym();
            gen(OPR, 12);
            if (sym == identsym) {
                int t = findintable(id);
                if (t != -1) {
                    if (table[t].kind == variable) {
                        gen(STO, table[t].adr);
                        getsym();
                        if (sym == semicolonsym)getsym();
                        else error(7);
                    }
                    else error(16);
                }
                else error(12);
            }
            else error(16);
        }
        else if (sym == writesym) {
            getsym();
            expression();
            if (sym == semicolonsym) {
                gen(OPR, 13);
                getsym();
            }
            else error(7);
        }
        else if (sym == returnsym) {
            getsym();
            expression();
            if (sym == semicolonsym) {
                gen(OPR, 0);
                getsym();
            }
            else error(7);
        }
        else if (sym == ifsym) {
            getsym();
            if (sym == lparensym) {
                getsym();
                condition();
                gen(JPC, 0);
                int t = pcnum - 1;
                if (sym == rparensym) {
                    getsym();
                    if (sym == beginsym) {
                        getsym();
                        statement();
                        if (sym == endsym) {
                            Pcode[t].a = pcnum;
                            getsym();
                        }
                        else error(15);
                    }
                    else error(10);
                }
                else error(9);
            }
            else error(8);
        }
        else if (sym == semicolonsym) getsym();
        else error(20);
    }
}

/*
* 生成 pcode
*/
void gen(fct kind, int a) {
    Pcode.push_back({ kind,a });
    pcnum=Pcode.size();
}

/*
* 处理函数体
*/
void block() {
    lvarnum = 0;
    while (sym == intsym) {
        getsym();
        intdeclaration();
        if (sym == semicolonsym) getsym();
        else error(0);
    }
    renter();
    if (proname == "main") Pcode[0].a = pcnum;
    gen(INT, parnum + lvarnum);
    statement();
    if (sym == endsym) {
        gen(OPR, 0);
        getsym();
    }
    else error(15);
}

/*
* 处理函数声明
*/
void proceduredeclaration() {
    parnum = 0;
    if (sym == identsym) {
        if (word.count(id) == 0) {
            if (isvaliddeclaration(procedure)) {
                proname = id;
                enter(procedure);
                getsym();
                if (sym == lparensym) {
                    getsym();
                    if (sym != rparensym) {
                        parnum = 0;
                        parameterdeclaration();
                        if (sym == rparensym) {
                            getsym();
                            if (sym == beginsym) {
                                getsym();
                                block();
                            }
                            else error(10);
                        }
                        else error(9);
                    }
                    else {
                        getsym();
                        if (sym == beginsym) {
                            getsym();
                            block();
                        }
                        else error(10);
                    }
                }
                else error(8);
            }
            else error(4);
        }
        else error(3);
    }
    else error(11);
}

vector<instruction> GetPCode() {
    return Pcode;
}

vector<string> GetEMS() {
    return ems;
}
vector<string> GetTable() {
    vector<string> tmp;
    for (int i = 0; i < txind; i++) {
        if (table[i].kind == constant) {
            tmp.push_back(to_string(i) + " " + table[i].name + " const " + to_string(table[i].val));
        }
        else if (table[i].kind == variable) {
            tmp.push_back(to_string(i) + " " + table[i].name + " var " + table[i].proname);
        }
        else {
            tmp.push_back(to_string(i) + " " + table[i].name + " func " + to_string(table[i].par) + " " + to_string(table[i].lvar));
        }
    }
    return tmp;
}

/*
* 编译器主函数
*/
int compiler() {
    init();
    ifstream codein;        // 代码文件
    codein.open("code.txt", ios::in);
    while (getline(codein, linecode)) code.push_back(linecode + '\n');
    if (code.size() == 0) {
        error(19);
        codein.close();
        return 0;
    }
    codein.close();
    linecode = code[linenum];
    gen(JMP, 0);
    getsym();
    while (sym != nulsym) {
        if (sym == constsym) {
            getsym();
            if (sym == intsym) constdeclaration();
            else error(2);
        }
        else if (sym == intsym) {
            getsym();
            proceduredeclaration();
        }
        else if (sym == semicolonsym) getsym();
        else if (sym == beginsym || sym == endsym) {
            error(21);
            getsym();
        }
        else error(30);
    }
    
    return issuccess;
}